/**
 * 
 */
package com.bbva.kyfb.kyfbutils.errores;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.PersistenceException;
import javax.sql.rowset.serial.SerialException;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.hibernate.NonUniqueObjectException;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.stereotype.Service;

import com.bbva.jee.arq.spring.core.gce.CodigoError;
import com.bbva.jee.arq.spring.core.gce.ExcepcionEjecucionAplicacion;
import com.bbva.jee.arq.spring.core.log.I18nLog;
import com.bbva.jee.arq.spring.core.log.I18nLogFactory;
import com.bbva.kyfb.kyfbdto.firmas.errores.Errores_kyfb_servicios;

/**
 * The Class ExceptionAspect.
 *
 * @author xe20973
 * 
 * Se encarga de centralizar
 */
@Aspect
@Service
public class ExceptionAspect implements Serializable {


	/** serialVersionUID. */
	private static final long serialVersionUID = 4178040668592604016L;

	/** LOGGER. */
	private static final I18nLog LOGGER = I18nLogFactory.getLogI18n(ExceptionAspect.class);

	
	/** aqui se guarda un registro de las trazas del hilo para evitar que se haga logging una vez por cada relanzamiento en el mismo stack. */
	private Set<Exception> throwsRecorded = new HashSet<Exception>();

	/**
	 * Trans
	 * 
	 * El pointcut se aplica a cualquier metodo de las clases del proyecto
	 * com.bbva.kyfb excepto a las de dominio
	 * 
	 * La exclusion de org.springframework es para que evite reinstrumentar clases
	 * que en realidad son proxys de spring previamente instrumentados
	 *
	 * @param joinPoint the join point
	 * @param exc the exc
	 * @throws ExcepcionEjecucionAplicacion the excepcion ejecucion aplicacion
	 */
	@AfterThrowing(pointcut="execution(* com.bbva.kyfb..* (..)) " +
			"&& !within(com.bbva.kyfb.firmas..*) " +
			"&& !within(org.springframework..*) " +
			"&& !within(com.bbva.kyfb..*Test*) " +
			"&& !within(com.bbva.jee..*) " +
			"&& !within(com.bbva.kyfb.kyfbutils.errores.ExceptionAspect) " +
			"&& !within(com.bbva.kyfb.*.dominio*..*)", throwing= "exc")
	public void ConsolidateException(final JoinPoint joinPoint, final Exception exc) throws ExcepcionEjecucionAplicacion {

		ExcepcionEjecucionAplicacion consolidada = null;
		
		if (!throwsRecorded.contains(exc)) {
            throwsRecorded.add(exc);
            
            Signature signature = joinPoint.getSignature();
    	    String methodName = signature.getName();
    	    String stuff = signature.toString();
    	    String arguments = Arrays.toString(joinPoint.getArgs());
    	    ;
            
            LOGGER.info("Se ha capturado una excepcion de tipo " + exc.getClass().getName() + 
    	    		" en el metodo" + signature.getDeclaringTypeName() + "." + methodName +
    	             " con los siguientes argumentos " + arguments +
    	             "\nsignature.toString(): " + stuff + 
    	             "\nla excepcion es: " + exc.getMessage(), exc);
            
            CodigoError codigoError = Errores_kyfb_servicios.SERVICIO_NO_DISPONIBLE;
            
            if (exc instanceof PersistenceException 
            		|| exc instanceof JpaSystemException 
            		|| exc instanceof NonUniqueObjectException
            		|| exc instanceof SQLException
            		|| exc instanceof SerialException) {
            	if (methodName.startsWith("delete")) {
            		codigoError = Errores_kyfb_servicios.ERROR_GENERICO_AL_ELIMINAR;
            	} else {
            		codigoError = Errores_kyfb_servicios.ERROR_BBDD;
            	}
    		}

            consolidada = new ExcepcionEjecucionAplicacion(codigoError, exc);
    		
            if (exc instanceof ExcepcionEjecucionAplicacion) {
            	consolidada = (ExcepcionEjecucionAplicacion) exc;
            }
    		throw consolidada;
        }
	}

	/**
	 * Gets the throws recorded.
	 *
	 * @return the throwsRecorded
	 */
	public Set<Exception> getThrowsRecorded() {
		return throwsRecorded;
	}

	/**
	 * Sets the throws recorded.
	 *
	 * @param throwsRecorded the throwsRecorded to set
	 */
	public void setThrowsRecorded(final Set<Exception> throwsRecorded) {
		this.throwsRecorded = throwsRecorded;
	}
	
	
}
